/*
 * $PROJECT_INFO$
 * 
 * $RELEASE_INFO$ 
 * 
 * $LICENSE_INFO$
 */
package org.sosie2.client.authentication;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.security.KeyStore;
import java.util.Properties;
import java.util.Vector;

import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.client.AxisClient;
import org.apache.axis.client.Call;
import org.apache.axis.configuration.NullProvider;
import org.apache.axis.message.SOAPBody;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.utils.XMLUtils;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.WSSecurityEngine;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecUsernameToken;
import org.apache.xml.security.c14n.Canonicalizer;
import org.sosie2.service.framework.Utils;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;


public class TestX509AndLicenseToken {
	/**
	 * @param secEngine
	 * 
	 */
	private static WSSecurityEngine secEngine = null;
	/**
	 * @param crypto
	 */
	private static WSSConfig wssConfig = WSSConfig.getDefaultWSConfig();
	private static Crypto crypto = null;
	/**
	 * 
	 */
	protected static Properties properties = new Properties();
	//private static String clientFile="/home/iis/bin/tomcat/webapps/axis/log/client.xml";
	
//	private static String url = "http://localhost:8081/cucu";
	private static String url = "http://localhost:8080/axis/services/SOSIESimpleAuthenticationWS";
	
	static final String emptySoapMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<SOAP-ENV:Body/>" + "</SOAP-ENV:Envelope>";
	//private static final String wmsGetCapabilities = "<wms:GetCapabilities service=\"WMS\" version=\"1.1.0\" xmlns:wms=\"http://www.opengis.net/wms\"/>";
	private static final String wfsGetCapabilities = "<wfs:DieMULE service=\"WFS\" version=\"1.0.0\" xmlns:wfs=\"http://www.opengis.net/wfs\"/>";
	private static final String wfsGetFeature2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><GetFeature xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengeospatial.net/wfs/1.0.0/WFS-basic.xsd\" xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns=\"http://www.opengis.net/wfs\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:ogc=\"http://www.opengis.net/ogc\" version=\"1.0.0\" service=\"WFS\" outputFormat=\"GML2\"><Query typeName=\"tiger:poly_landmarks\"></Query></GetFeature>";
	
	private static String username = "gabi";
	private static String password = "geheim";
	
	private static final boolean ADD_USERNAME_TOKEN = true;
	private static final boolean ADD_DIGITAL_SIGNATURE = false;
	
	private static final boolean SMARTCARD = false;
	private static final String SMARTCARD_USERNAME = "Cristian OPINCARU's Certificate Authority ID";
	private static final String SMARTCARD_PIN = "1234";	 
		
	public static void main(String args[]) throws Exception {
		try {			
			MessageContext msgContext;
			
			AxisClient tmpEngine = new AxisClient(new NullProvider());
			@SuppressWarnings("unused") WSSecurityEngine secEngine = WSSecurityEngine.getInstance();
			
			msgContext = new MessageContext(tmpEngine);
			InputStream in = new ByteArrayInputStream(emptySoapMsg.getBytes());
			Message msg = new Message(in);
			msg.setMessageContext(msgContext);	        	        
			 
			// Parse the request & add body to SOAP message
			//String request = wfsGetFeature2;
			String request = wfsGetCapabilities;
			StringReader strReader = new StringReader(request);			
			Document document = XMLUtils.newDocument(new InputSource (strReader));
			
			// Add request to SOAP body
			SOAPBody body = (SOAPBody) msg.getSOAPBody();
			body.addDocument(document);				
			
			
			Document envelope = msg.getSOAPEnvelope().getAsDocument();
			WSSecHeader hSec = new WSSecHeader("",false);
			hSec.insertSecurityHeader(envelope);

// -------------------------------- Add certificate & Sign the body ----------------------------------
			if (ADD_DIGITAL_SIGNATURE) {
				Merlin crypto = null;
				if (SMARTCARD) {
					// Load the keystore -> For Smartcard			
					KeyStore ks = KeyStore.getInstance("PKCS11");
					String pin = "1234";
					ks.load(null, pin.toCharArray());						
					crypto = (Merlin) CryptoFactory.getInstance("org.apache.ws.security.components.crypto.Merlin",(Properties) null);
					crypto.setKeyStore(ks);
					
					username = SMARTCARD_USERNAME;
					password = SMARTCARD_PIN;
				} else {			
					// For file keystore				
					crypto = (Merlin) CryptoFactory.getInstance("ows4.client.crypto.properties");
				}
				
				// Sign the body & insert the signature			
				org.apache.ws.security.SOAPConstants soapConstants = org.apache.ws.security.SOAPConstants.SOAP11_CONSTANTS;
				WSSecSignature signer = new WSSecSignature();			
				signer.setUserInfo(username, password);			
				signer.setUseSingleCertificate(true);
				//	signer.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
				Vector parts = new Vector();
				WSEncryptionPart part = new WSEncryptionPart(soapConstants.getBodyQName().getLocalPart(),
				                                             soapConstants.getEnvelopeURI(),
				                                             "Content");
				parts.add(part);
				signer.setParts(parts); // this is optional since the body is signed by default					
				envelope = signer.build(envelope, crypto, hSec);
			}
// -------------------------------- Add certificate & Sign the body ----------------------------------			

// -------------------------------- Insert Username Token ----------------------------------			
			// Insert Username Token
			if (ADD_USERNAME_TOKEN) {
				WSSecUsernameToken addtoken = new WSSecUsernameToken();
				addtoken.setUserInfo(username,password);
				addtoken.setPasswordType(org.apache.ws.security.WSConstants.PASSWORD_DIGEST);			
				envelope = addtoken.build(envelope, hSec);
			}
// -------------------------------- Insert Username Token ----------------------------------			
																						
			
//			Make the call
			SOAPMessage soapMsg = Utils.toMessage(envelope);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			soapMsg.writeTo(baos);
			msg = new Message(baos.toString());
			msg.setMessageContext(msgContext);			
			
			Call call = new Call(url);
			System.out.println("Calling ...");
			call.setProperty(WSHandlerConstants.MUST_UNDERSTAND, "false");
			call.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, "org.sosie2.client.authentication.SimpleCallbackHandler");
			SOAPEnvelope response = (SOAPEnvelope)call.invoke(msg);							
			dumpXML("\n\n\nResponse:",response.getAsDOM().getOwnerDocument());
		} catch (AxisFault f) {			
			f.printStackTrace();			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	public static void dumpXML(String s, Document d) throws Exception {
		// Dump response
		Transformer transformer = TransformerFactory.newInstance().newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		transformer.transform(new DOMSource(d), new StreamResult(baos));
		
		System.out.println(s);
		System.out.print(baos.toString());
	}
	
	public static Message toMessage(Document doc) throws Exception {
		Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
		byte[] canonicalMessage = c14n.canonicalizeSubtree(doc);
		ByteArrayInputStream in = new ByteArrayInputStream(canonicalMessage);
		MessageFactory factory = org.apache.axis.soap.MessageFactoryImpl.newInstance();
		return (Message) factory.createMessage(null, in);        
	}	
}
